package eva.savant.core.view.util;

import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.hssf.usermodel.*;
import java.io.*;

public class ExcelUtils {

    public static int LEVELS_COLUMN = 1;
    
    /**Agrupador de Niveles de hojas EXCEL
     *
     * Los totales deben estar ubicados en las cabeceras de los grupos, no así en el pie.
     * Esto con la finalidad de poder ver los totales al ocultar los detalles del mismo.
     *
     * El orden del archivo debe ser el siguiente:
     * <pre>
     *    1	   8111 NIVEL 1                             0	35,654.93
     *    2	   BO0101 NIVEL 1.1                         0	35,157.61
     *    3	   8119,  Dulce De Leche 1.1.1	            0	152.22
     *    4	   811901,  Dulce de Leche 1.1.1.1	        0	152.22
     *    5	   F03508	DULCE DE LECHE MONKY X 13 	    0   152.22
     *    5	   F03507	DULCE DE LECHE MONKY 12  	    0   152.22
     *    3	   8121,  Jugo Lacteo	1.1.20              0   59.14
     *    4	   812101,  Jugo Lacteo	1.1.2.1             0	59.14
     *    5	   F04519	PILFRUT LIMON X 1 LT.(BOLSA)	0	59.14
     *    5	   F04519	PILFRUT LIMON X 2 LT.(BOLSA)	0	59.14
     *    5	   F04519	PILFRUT LIMON X 3 LT.(BOLSA)	0	59.14
     *
     * </pre>
     *
     * @param bytes el archivo Excel en flujo de bytes, esto con la finalidad de hacer mas portable.
     * @param levelsNumber numero de niveles que tiene el archivo excel (para el ejemplo 5)
     * @param groupFromLevel nivel desde el cual se realizara el agrupamiento.
     * @param collapsed arbol abierto o cerrado despues de generar el archivo xls.
     * @return arreglo de byte[] con el mismo archivo XLS solo que agrupado.
     * @throws Exception en caso de error
     */

    public static byte[] groupExcelFile(byte[] bytes  , int levelsNumber , int groupFromLevel, boolean collapsed) throws Exception {

        ByteArrayOutputStream outputStream  = new ByteArrayOutputStream();

        byte[] output;


        if(bytes == null || bytes.length == 0) throw new Exception("The length is zero");


        InputStream inputStream = new ByteArrayInputStream(bytes);

        POIFSFileSystem fs = new POIFSFileSystem(inputStream);

        HSSFWorkbook wb = new HSSFWorkbook(fs);


        if(wb.getNumberOfSheets() == 0) throw new Exception("El archivo no tiene contenido");

        HSSFSheet sheet = wb.getSheetAt(0);

        int rowsNumber = sheet.getPhysicalNumberOfRows(); //iterar todas las filas con datos.

        HSSFRow row;

        HSSFCell cell;

        int numElsGroup = 0;

        int initialRow = 0;

        boolean groupBlock = false;

        boolean markFirstBoundary = true;

        boolean groupingNextOnes = false;

        int level;

        int currentLevel = levelsNumber;

                
        while(currentLevel > 0 && currentLevel >= groupFromLevel) { //avanzar por niveles

            for(int i = 0; i < rowsNumber ; i++) { //recorrer de arriba a abajo todas las filas del archivo

                row = sheet.getRow(i); //recuperar el valor del nivel de la celda de la columna B
                
                cell = row.getCell(LEVELS_COLUMN); //columna que contiene la numeracion para los niveles

                if(cell==null || (level = (int)cell.getNumericCellValue())==0 ) continue; //si no contiene valor, saltar

                if(levelsNumber == currentLevel ) { //si es el ultimo nivel el que se esta tratando

                    if(level == levelsNumber ) {

                        if(markFirstBoundary) { //si se debe marcar el primer limite

                            initialRow = i; //marcamos el primer limite

                            markFirstBoundary = false;
                        }

                        numElsGroup++;

                        row = sheet.getRow(i+1);

                        if(row != null) {

                            cell = row.getCell(1);

                            if (cell == null || (int) cell.getNumericCellValue() != level ) groupBlock = true;

                        } else
                            groupBlock = true;
                    }

                } else {

                    if(level == currentLevel) {

                        if(markFirstBoundary) { //se encuentra el primer limite

                            initialRow = i;

                            markFirstBoundary = false;

                            groupingNextOnes = true;
                        } else
                            numElsGroup++;

                    } else if(groupingNextOnes && level >= currentLevel) {

                        numElsGroup++;

                    } else if(groupingNextOnes && level < currentLevel) {

                        groupBlock = true;

                        groupingNextOnes = false;

                        numElsGroup++;

                    }

                    row = sheet.getRow(i+1);

                    if(row != null) {

                        cell = row.getCell(1);

                        if (cell == null || (int) cell.getNumericCellValue() == 0) {

                            groupBlock = true;

                            numElsGroup++;

                            groupingNextOnes=false;
                        }

                    } else {

                        groupBlock = true;

                        numElsGroup++;

                        groupingNextOnes=false;
                    }

                }

                if(groupBlock && initialRow > 0  && numElsGroup > 0) {

                    sheet.groupRow(initialRow , initialRow + numElsGroup - 1);

                    sheet.setRowGroupCollapsed(initialRow, collapsed);

                    numElsGroup         = 0;

                    initialRow          = 0;

                    markFirstBoundary   = true;

                    groupBlock          = false;
                }                

            }

            currentLevel--;
        }

        sheet.setColumnHidden(LEVELS_COLUMN , true);

        sheet.setAlternativeExpression(false);

        wb.write(outputStream);

        output = outputStream.toByteArray();

        outputStream.close();

        return output;
    }

    public static byte[] groupExcelFile(byte[] bytes  , int levelsNumber) throws Exception {

        return groupExcelFile(bytes,levelsNumber,1,true);
    }

    public static byte[] getBytesFromFile(File file) throws IOException {

        InputStream is = new FileInputStream(file);

        long length = file.length();

        byte[] bytes = new byte[(int)length];

        int offset = 0;

        int numRead;

        while (offset < bytes.length && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) offset += numRead;

        if (offset < bytes.length) throw new IOException("Could not completely read file "+file.getName());

        is.close();

        return bytes;
    }

    public static void main(String arg[]) throws Exception {

        File f= new File(".");

        System.out.println("PATH = "+f.getAbsolutePath());

        byte[] bytes = getBytesFromFile(new File("test-in.xls"));

        int fromLevel = 3;

        byte[] result = groupExcelFile(bytes , 4 , fromLevel , true);

        FileOutputStream fileOut = new FileOutputStream( "test-out.xls" );

        fileOut.write(result);

        fileOut.flush();

        fileOut.close();
    }

}
